
#include "rpcserver.h"
#include <google/protobuf/descriptor.h>
#include "rpcheader.pb.h"
#include "redis/redisclient.h"

namespace TinyRPC{

void TinyRPCServer::init(const std::string &filePath){
    m_config.init(filePath);
    std::string redis_ip = m_config.LookUp<std::string>("redis.ip");
    int redis_port = m_config.LookUp<int>("redis.port");
    m_register.init(redis_ip, redis_port);

    int listen_port = m_config.LookUp<int>("listen.port");
    m_serverAddress = INetAddress("127.0.0.1", listen_port);
}
void TinyRPCServer::registService(google::protobuf::Service * service){
    
    const google::protobuf::ServiceDescriptor * serviceDescrip = service->GetDescriptor();
    std::string serviceName = serviceDescrip->name();

    int methodCount = serviceDescrip->method_count();
    ServiceInfo serviceInfo;
    for(int i=0;i<methodCount; i++){
        
        serviceInfo.m_service = service;
        const google::protobuf::MethodDescriptor * methodDescrip = serviceDescrip->method(i);
        serviceInfo.m_methodMap.emplace(methodDescrip->name(), methodDescrip);   
    }
    m_serviceMap.insert({serviceName, serviceInfo});

}


void TinyRPCServer::handleRPCConnection(SocketChannelPtr socketChannel){
    BufferPtr buffer = socketChannel->read();
    /**
     * header size
     *  rpc header
     *      service_name
     *      method_name
     *      arg_size;
     */
    std::string recv_str = buffer->read();

    TinyRpcHeader header;
    uint32_t header_size;
    recv_str.copy((char*)&header_size, 4, 0);

    std::string header_str = recv_str.substr(4,header_size);
    std::string arg_str = recv_str.substr(4+header_size, header.args_size());
    header.ParseFromString(header_str);

    auto it_service = m_serviceMap.find(header.service_name());
    auto it_method = it_service->second.m_methodMap.find(header.method_name());
    
    google::protobuf::Service * service = it_service->second.m_service;
    const google::protobuf::MethodDescriptor * method = it_method->second;

    google::protobuf::Message * request = service->GetRequestPrototype(method).New();
    request->ParseFromString(arg_str);

    google::protobuf::Message * response = service->GetResponsePrototype(method).New();
    // 给下面的mthod方法的调用
    google::protobuf::Closure * closure = google::protobuf::NewCallback<TinyRPCServer,SocketChannelPtr,google::protobuf::Message *>(this, &TinyRPCServer::sendRpcResponse, socketChannel, response);
    service->CallMethod(method, nullptr, request, response, closure);
    // 寻找
    // socketChannel.write(str);
    // std::cout<<str<<std::endl;
}

void TinyRPCServer::sendRpcResponse(SocketChannelPtr socketChannel, google::protobuf::Message * response){
    std::string response_str;
    response->SerializeToString(&response_str);
    socketChannel->write(response_str);
}
void TinyRPCServer::accept(){
    
    ServerSocketChannel server = ServerSocketChannel::CreateAndBind(m_serverAddress);
    server.listen();
    while (1)
    {
        SocketChannelPtr socketChannel = server.accept();
        // loop->runInLoop(Task(std::bind(&TcpServer::echo, this, socketChannel), true));
        std::cout<<"accept socket"<<std::endl;
        // 这里会存在问题， 即在一个协程里面调用另一个协程
        getNextLoop()->runInLoop(Task(std::bind(&TinyRPCServer::handleRPCConnection, this, socketChannel), true));
        socketChannel.reset();
    }
}
void TinyRPCServer::start(){
    // 向注册中心上报
    m_eventLoopPool.start();
    getNextLoop()->runInLoop(Task(std::bind(&TinyRPCServer::accept, this), true));
    
    m_register.regist(m_serviceMap, m_serverAddress);
    // 加入心跳机制
    getNextLoop()->runInLoop(Task(std::bind(&RedisRegister::heartbeat, &(this->m_register)), true));
    m_baseLoopPtr->loop();
}

}
